package com.forestar.mapControl.utils.mapConvert;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.view.Surface;
import android.widget.Toast;

import com.forestar.mapControl.mappool.MapCachePool2;
import com.forestar.mapControl.mappool.MzTileBean;
import com.forestar.mapControl.mapview.CacheCanvas;
import com.forestar.mapControl.mapview.multitouch.LayerInfo;
import com.forestar.mapControl.mapview.multitouch.TestTextureViewRefreshListener;
import com.forestar.mapControl.utils.LogD;
import com.forestar.mapControl.utils.bitmapcache.CacheableBitmapDrawable;
import com.forestar.mapControl.utils.mapConvert.convert.ConvertHelper2;
import com.forestar.mapControl.utils.mapConvert.geometry.Envelope;
import com.forestar.mapControl.utils.mapConvert.geometry.GeoPoint;
import com.forestar.mapControl.utils.mapConvert.task.ConfigurablePriorityThreadFactory;
import com.forestar.mapControl.utils.tile.TileCoord;
import com.forestar.mapControl.utils.tile.TileSchema;
import com.forestar.mapControl.utils.tile.TilesIndexBound;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import static com.forestar.mapControl.mappool.MzTileBean.LOAD_STATE_COMPLETE;
import static com.forestar.mapControl.mappool.MzTileBean.LOAD_STATE_FAIL;
import static com.forestar.mapControl.mappool.MzTileBean.LOAD_STATE_LOADING;
import static com.forestar.mapControl.mappool.MzTileBean.LOAD_STATE_NO;
import static com.forestar.mapControl.mappool.MzTileBean.LOAD_STATE_SHOWING;
import static com.forestar.mapControl.utils.mapConvert.convert.ConvertHelper2.TIANDITU_SAT_KEY2;
import static com.forestar.mapControl.utils.mapConvert.convert.ConvertHelper2.TIANDITU_SAT_KEY3;
import static com.forestar.mapControl.utils.mapConvert.convert.ConvertHelper2.TIANDITU_SAT_KEY4;
import static com.forestar.mapControl.utils.mapConvert.convert.ConvertHelper2.TIANDITU_SAT_KEY5;
import static com.forestar.mapControl.utils.mapConvert.task.DrawForTask.heightScreen;
import static com.forestar.mapControl.utils.mapConvert.task.DrawForTask.widthScreen;

/**
 * created by  xzk
 * 2020/7/27   17:02
 * 说明:  从这里，开始解析地图位置，发起任务请求
 */
public class MapConvert2 {

    private CacheCanvas cacheCanvas;
    private List<CacheCanvas> cacheCanvasList;

    private List<LayerInfo> layerForNeedList;

    protected TileSchema tileSchema;
    protected TileSchema tileSchema2;
    private float screenPPI;

    private Paint tempPaint = new Paint();
    private Paint emtryPaint = new Paint();

    private TestTextureViewRefreshListener refreshListener;

    //读取瓦片线程池
    private ThreadPoolExecutor loadTilePool;
    private final ConvertHelper2 convertHelper;

    private Context context;

    private Bitmap colorBitmap;
    private Bitmap colorRedBitmap;
    private Bitmap colorGreenBitmap;


    public List<String> getMillSum() {
        return new ArrayList<>();
    }

    public void initData(CacheCanvas targetCanvas, TestTextureViewRefreshListener refreshListener) {
        this.tileSchema = tileSchema;
        this.refreshListener = refreshListener;
    }

    public void initData(CacheCanvas cacheCanvas, CacheCanvas cache2Canvas, List<CacheCanvas> cacheLayerList,Context context
            , TestTextureViewRefreshListener refreshListener) {


        this.tileSchema = ConvertHelper2.buildTileSchema();
        this.tileSchema2 = ConvertHelper2.buildTileSchema2();
        this.cacheCanvasList = cacheLayerList;
        this.context = context;

        //构建图层信息  标注层
        layerForNeedList = new ArrayList<>();

        //地图层
        LayerInfo layerInfo1 = new LayerInfo();
        layerInfo1.setSourceKey(TIANDITU_SAT_KEY5);
        layerInfo1.setCanvasLayer(cacheCanvasList.get(0));
        layerInfo1.setIndex(0);
        layerForNeedList.add(layerInfo1);

        // 标注层
        LayerInfo layerInfo2 = new LayerInfo();
        layerInfo2.setSourceKey(TIANDITU_SAT_KEY4);
        layerInfo2.setCanvasLayer(cacheCanvasList.get(1));
        layerInfo2.setIndex(1);
        layerForNeedList.add(layerInfo2);

        this.cacheCanvas = cacheCanvas;
        this.refreshListener = refreshListener;
        colorBitmap = MapCachePool2.getInstance().createSolidColorBitmap();
        colorRedBitmap = MapCachePool2.getInstance().createSolidRedColorBitmap();
        colorGreenBitmap = MapCachePool2.getInstance().createSolidGreenColorBitmap();
    }

    public MapConvert2() {
        //设定最大
        loadTilePool = new ThreadPoolExecutor(2, 60, 10L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>(300), new ConfigurablePriorityThreadFactory(Thread.NORM_PRIORITY, "加载影像"),
                new ThreadPoolExecutor.DiscardOldestPolicy());

        convertHelper = new ConvertHelper2();

        emtryPaint.setColor(Color.RED);
        emtryPaint.setStyle(Paint.Style.STROKE);
        emtryPaint.setTextSize(10);
        emtryPaint.setStrokeWidth(5);
    }

    public float getScreenPPI(Context mContext) {
        MapCachePool2.getInstance().setmContextAndInitCache(mContext);
        this.screenPPI = mContext.getResources().getDisplayMetrics().xdpi;
        return this.screenPPI;
    }

    private AtomicInteger executorTaskNum = new AtomicInteger(0);
    private MapViewTransform transformNew2;

    //显示状态序列号，用来判断是否是最新的显示数据
    private AtomicInteger uniqueDisplayStatusId = new AtomicInteger(0);

    public void loadingAllForScreen(MapViewTransform transform, MapViewTransform transform2) {

        this.transformNew2 = transform2;
        if (null == transformNew2 || tileSchema == null) {
            return;
        }
        int level2 = tileSchema2.getBestLevelByResolution(transformNew2.getResolutionInMapUnit(), screenPPI);
        Envelope envlope2 = transformNew2.getViewBound();
        TilesIndexBound tilesBound2 = tileSchema2.getTilesIndexBound(level2, envlope2.getXMin(), envlope2.getXMax(), envlope2.getYMin(), envlope2.getYMax());
        LogD.d("本次绘制的区域:" + tilesBound2.toString() + "----");
        if (tilesBound2.isEmpty()) {
            return;
        }
//      总共10层
        long start = System.currentTimeMillis();
        long endDraw = 0;

        //更新id
        uniqueDisplayStatusId.incrementAndGet();
        //这里初始化
        MapCachePool2.getInstance().removeAllTask();

        int maxLength = 0;
        int hasDraw = 0;
        cacheCanvas.getCanvas().drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
        for (int i = 0; i < cacheCanvasList.size(); i++) {
            cacheCanvasList.get(i).getCanvas().drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
        }

        for (int i = 0; i < layerForNeedList.size(); i++) {
            LayerInfo layerInfo = layerForNeedList.get(i);
            for (int y = tilesBound2.minTileY; y <= tilesBound2.maxTileY; y++) {
                for (int x = tilesBound2.minTileX; x <= tilesBound2.maxTileX; x++) {
                    String cacheKey = MzTileBean.buildCacheKey(layerInfo.getSourceKey(), tilesBound2.level, x, y);
                    MzTileBean tile = layerInfo.getLayerForTile().get(cacheKey);
                    if (null != tile) {
                    } else {
                        tile = new MzTileBean(layerInfo.getSourceKey(), tilesBound2.level, x, y, layerInfo);
                        layerInfo.getLayerForTile().put(tile.getCacheKey(), tile);
                    }
                    //针对，没有显示的图像，绘制到缓存上，，，或者放进任务池，等待更新请求
                    if (hasDraw <= maxLength) {
                        BitmapDrawable forShow = MapCachePool2.getInstance().getBitmapForShow(tile);
                        if (null != forShow) {
                            //在绘制 数量个图像的时候，使用
                            drawTile2(tile);
                            cacheCanvas.getCanvas().drawBitmap(forShow.getBitmap(), null, tile.getTileRect(), tempPaint);
                            tile.setState(LOAD_STATE_SHOWING);
                            hasDraw++;
                        } else {
                            //放入任务池
                            MapCachePool2.getInstance().putForRequestTaskIfNeed(tile);
                        }
                    } else {
                        //放入任务池
                        MapCachePool2.getInstance().putForRequestTaskIfNeed(tile);
                    }
                }
            }
        }
        LogD.d("有效图层数量:" + hasDraw);
        endDraw = System.currentTimeMillis();
        ///  30-38ms一次
        LogD.d("单次拉取瓦片:效率1：" + (endDraw - start) + "--》有效图层数量:" + hasDraw);
        //移动过程中，增加多任务，因为，移动过程中，需要不停加载，而静止下来时，只需要把已经存在的任务加载完毕就好了
        if (null != refreshListener) {
            refreshListener.refreshTextureView();
        }
        //根据需要的请求任务数，增加线程池任务数
        int lastQueueSize = MapCachePool2.getInstance().getmWorking().size() - loadTilePool.getQueue().size();
        for (int i = 0; i < lastQueueSize; i++) {
            //添加任务时，得按照没有的才添加的策略
            loadTilePool.execute(new LoadTileTask());
        }
    }

//    创建线程池，不停的往缓存画布上增量绘制图形，试试看能不能随着手指移动换图形

    //通过关系，计算瓦片在哪个位置
    public void drawTile2(MzTileBean tile) {
        final Rect mTileRect = new Rect();
        TileCoord tileCoord = tileSchema2.getTileCoord(tile.getLevel(), tile.getX(), tile.getY());
        // 换算到屏幕坐标
        GeoPoint gpUpperLeft;
        GeoPoint gpLowerRight;
        gpUpperLeft = new GeoPoint(tileSchema2.getCoordinateSystem(), tileCoord.minX, tileCoord.maxY);
        gpLowerRight = new GeoPoint(tileSchema2.getCoordinateSystem(), tileCoord.maxX, tileCoord.minY);
        PointF upperLeft = this.transformNew2.mapPoint2ScreenPoint(gpUpperLeft);
        PointF lowerRight = this.transformNew2.mapPoint2ScreenPoint(gpLowerRight);

        mTileRect.set((int) upperLeft.x
                , (int) upperLeft.y
                , (int) lowerRight.x
                , (int) lowerRight.y);
        //保存当前图片显示的区域
        tile.setTileRect(mTileRect);
    }

    //保存当前边界，以及，更新边界信息 算法(osmdroid是如何做到，边界时敏感的问题)

    /**
     * 加载任务执行线程
     * <p>
     * 只执行加载瓦片到内存中，并不进行图片显示
     */
    private class LoadTileTask implements Runnable {

        @Override
        public void run() {
            executorTaskNum.decrementAndGet();
            MzTileBean workTask = MapCachePool2.getInstance().getFirstWorkTask();
            if (null != workTask && workTask.getState() == LOAD_STATE_NO) {
                String cacheKey = workTask.getCacheKey();

                workTask.setState(LOAD_STATE_LOADING);
                LayerInfo layerInfo = workTask.getLayerID();

                CacheableBitmapDrawable singleTile = convertHelper.getSingleTile(workTask);
                if (null != singleTile && singleTile.isBitmapValid()) {

                    workTask.setState(LOAD_STATE_COMPLETE);
                    MapCachePool2.getInstance().removeTask(workTask);

                    //绘制之前，先把最新位置计算一遍
                    drawTile2(workTask);

                    //超出屏幕区域就不绘制了 tileRect
                    Rect tileRect = workTask.getTileRect();
                    singleTile.setBounds(tileRect);
                    //绘制到对应的画布上
                    singleTile.draw(layerInfo.getCanvasLayer().getCanvas());

                    if(layerInfo.getIndex()>0){
                        //底层往上，可以等画完了，再显示
                        if(layerInfo.drawComplete()){
                            //按顺序画到缓存上，，，这里倒不是说，缓存会有什么影响，而是对于呈现效果来讲，是不太显眼
                            for (int i = 0; i < layerForNeedList.size(); i++) {
                                LayerInfo layerOther = layerForNeedList.get(i);
                                cacheCanvas.getCanvas().drawBitmap(layerOther.getCanvasLayer().getBitmap(), 0, 0, tempPaint);
                            }
                            if (null != refreshListener) {
                                refreshListener.refreshTextureView();
                            }
                        }
                    }else {
                        //按顺序画到缓存上，，，这里倒不是说，缓存会有什么影响，而是对于呈现效果来讲，是不太显眼
                        for (int i = 0; i < layerForNeedList.size(); i++) {
                            LayerInfo layerOther = layerForNeedList.get(i);
                            cacheCanvas.getCanvas().drawBitmap(layerOther.getCanvasLayer().getBitmap(), 0, 0, tempPaint);
                        }
                        if (null != refreshListener) {
                            refreshListener.refreshTextureView();
                        }
                    }
                } else {
                    workTask.setState(LOAD_STATE_FAIL);
                    //绘制之前，先把最新位置计算一遍
                    drawTile2(workTask);
                    LogD.d("下载位置:下载失败:"+workTask.getTileRect());
                    cacheCanvas.getCanvas().drawText("下载失败:",workTask.getTileRect().left+workTask.getTileRect().width()/2,
                            workTask.getTileRect().top+workTask.getTileRect().height()/2,emtryPaint);
                }
                if (MapCachePool2.getInstance().getmWorking().size() > 0) {
                    loadTilePool.execute(new LoadTileTask());
                }
            } else {
                LogD.d("地图效率 没有任务了:" + (workTask == null ? "" : (workTask.getCacheKey() + workTask.getState())));
            }
        }
    }

}

//            //放入屏幕外围的预加载任务(移动过程中，不加载外围瓦片，移动就是外围)
//            for (int y = tilesBound.minTileY-yMore; y <= tilesBound.maxTileY+yMore; y++) {
//                for (int x = tilesBound.minTileX-xMore; x <= tilesBound.maxTileX+xMore; x++) {
//                    //接入外围区域
//                    if(y>tilesBound.minTileY && y<tilesBound.maxTileY && x > tilesBound.minTileX && x < tilesBound.maxTileX ){
//                        continue;
//                    }
//                    final MzTileBean tile = new MzTileBean(TIANDITU_SAT_KEY2, tilesBound.level, x, y,layerID);
//                    drawTile(tile);
////                   //外围区域不绘制，只预加载
//                    MapCachePool2.getInstance().putForRequestTaskIfNeed(tile);
//                }
//            }